package sip

import (
	"fmt"
	"regexp"
	"strings"
)

// Uri URI信息(RFC3261-19.1)
// 格式：sip:user:password@host:port;uri-parameters?headers
type Uri struct {
	// True if and only if the URI is a SIPS URI.
	Encrypted bool

	// The user part of the URI: the 'joe' in sip:joe@bloggs.com
	// This is a pointer, so that URIs without a user part can have 'nil'.
	User MaybeString

	// The password field of the URI. This is represented in the URI as joe:hunter2@bloggs.com.
	// Note that if a URI has a password field, it *must* have a user field as well.
	// This is a pointer, so that URIs without a password field can have 'nil'.
	// Note that RFC 3261 strongly recommends against the use of password fields in SIP URIs,
	// as they are fundamentally insecure.
	Password MaybeString

	// The host part of the URI. This can be a domain, or a string representation of an IP address.
	Host string

	// The port part of the URI. This is optional, and so is represented here as a pointer type.
	Port *Port

	// Any parameters associated with the URI.
	// These are used to provide information about requests that may be constructed from the URI.
	// (For more details, see RFC 3261 section 19.1.1).
	// These appear as a semicolon-separated list of key=value pairs following the host[:port] part.
	Params Params

	// Any headers to be included on requests constructed from this URI.
	// These appear as a '&'-separated list at the end of the URI, introduced by '?'.
	// Although the values of the map are MaybeStrings, they will never be NoString in practice as the parser
	// guarantees to not return blank values for header elements in SIP URIs.
	// You should not set the values of headers to NoString.
	Headers Params
}

// sip contact 匹配正则
var contactRegExp *regexp.Regexp

func init() {
	contactRegExp = regexp.MustCompile("\\<(.*?)\\>")
}

// NewURI 解析 URI 字符
func NewURI(s string) (*Uri, error) {
	m := contactRegExp.FindStringSubmatch(s)
	if len(m) >= 2 {
		s = m[1]
	}

	uri := &Uri{}
	if err := uri.parseSipUri(s); err != nil {
		return nil, err
	}
	return uri, nil
}

// ParseSipUri converts a string representation of a SIP or SIPS URI into a SipUri object.
func (uri *Uri) parseSipUri(uriStr string) (err error) {
	// Store off the original URI in case we need to print it in an error.
	uriStrCopy := uriStr

	// URI should start 'sip' or 'sips'. Check the first 3 chars.
	if strings.ToLower(uriStr[:3]) != "sip" {
		err = fmt.Errorf("invalid SIP uri protocol name in '%s'", uriStrCopy)
		return
	}
	uriStr = uriStr[3:]
	// check if URI authority part exist after scheme
	if len(uriStr) < 1 {
		err = fmt.Errorf("uri too short to parse. '%s'", uriStrCopy)
		return
	}
	if strings.ToLower(uriStr[0:1]) == "s" {
		// URI started 'sips', so it's encrypted.
		uri.Encrypted = true
		uriStr = uriStr[1:]
	}
	// check if URI authority part exist after scheme
	if len(uriStr) < 1 {
		err = fmt.Errorf("uri too short to parse. '%s'", uriStrCopy)
		return
	}
	// The 'sip' or 'sips' protocol name should be followed by a ':' character.
	if uriStr[0] != ':' {
		err = fmt.Errorf("no ':' after protocol name in SIP uri '%s'", uriStrCopy)
		return
	}
	uriStr = uriStr[1:]

	// SIP URIs may contain a user-info part, ending in a '@'.
	// This is the only place '@' may occur, so we can use it to check for the
	// existence of a user-info part.
	endOfUserInfoPart := strings.Index(uriStr, "@")
	if endOfUserInfoPart != -1 {
		// A user-info part is present. These take the form:
		//     user [ ":" password ] "@"
		endOfUsernamePart := strings.Index(uriStr, ":")
		if endOfUsernamePart > endOfUserInfoPart {
			endOfUsernamePart = -1
		}

		if endOfUsernamePart == -1 {
			// No password component; the whole of the user-info part before
			// the '@' is a username.
			if username, er := Unescape(uriStr[:endOfUserInfoPart], EncodeUserPassword); er == nil {
				uri.User = String{Str: username}
			} else {
				err = fmt.Errorf("unescape username: %w", er)
				return
			}
		} else {
			if username, er := Unescape(uriStr[:endOfUsernamePart], EncodeUserPassword); er == nil {
				uri.User = String{Str: username}
			} else {
				err = fmt.Errorf("unescape username: %w", er)
				return
			}
			if password, er := Unescape(uriStr[endOfUsernamePart+1:endOfUserInfoPart], EncodeUserPassword); er == nil {
				uri.Password = String{Str: password}
			} else {
				err = fmt.Errorf("unescape password: %w", er)
				return
			}
		}
		uriStr = uriStr[endOfUserInfoPart+1:]
	}

	// A ';' indicates the beginning of a URI params section, and the end of the URI itself.
	endOfUriPart := strings.Index(uriStr, ";")
	if endOfUriPart == -1 {
		// There are no URI parameters, but there might be header parameters (introduced by '?').
		endOfUriPart = strings.Index(uriStr, "?")
	}
	if endOfUriPart == -1 {
		// There are no parameters at all. The URI ends after the host[:port] part.
		endOfUriPart = len(uriStr)
	}

	uri.Host, uri.Port, err = ParseHostPort(uriStr[:endOfUriPart])
	uriStr = uriStr[endOfUriPart:]
	if err != nil {
		return
	} else if len(uriStr) == 0 {
		uri.Params = NewParams()
		uri.Headers = NewParams()
		return
	}

	// Now parse any URI parameters.
	// These are key-value pairs separated by ';'.
	// They end at the end of the URI, or at the start of any URI headers
	// which may be present (denoted by an initial '?').
	var uriParams Params
	var n int
	if uriStr[0] == ';' {
		uriParams, n, err = ParseParams(uriStr, ';', ';', '?', true, true)
		if err != nil {
			return
		}
	} else {
		uriParams, n = NewParams(), 0
	}
	uri.Params = uriParams
	uriStr = uriStr[n:]

	// Finally parse any URI headers.
	// These are key-value pairs, starting with a '?' and separated by '&'.
	var headers Params
	headers, n, err = ParseParams(uriStr, '?', '&', 0, true, false)
	if err != nil {
		return
	}
	uri.Headers = headers
	uriStr = uriStr[n:]
	if len(uriStr) > 0 {
		err = fmt.Errorf("internal error: parse of SIP uri ended early! '%s'",
			uriStrCopy)
		return // Defensive return
	}

	return
}
